package net.csdn.business.common.oauth2.component;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.constant.RedisConstants;
import net.csdn.business.common.domain.vo.oauth.OauthClientInfoVo;
import net.csdn.business.common.oauth2.service.IOauthClientInfoService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.util.StringUtils;

import java.util.Map;


/**
 * @Desc: 扩展jdbcClientDetailsService
 * @ClassName: CustomClientDetailsServiceImpl
 * @Author: yourleige@163.com
 * @Date: 2022/9/27 11:41
 */
@Slf4j
@RequiredArgsConstructor
public class CustomClientDetailsService implements ClientDetailsService {

	private final IOauthClientInfoService oauthClientInfoService;

	/**
	 * 重写原生方法支持redis缓存
	 * @param clientId
	 * @return ClientDetails
	 * @throws InvalidClientException
	 */
	@Override
	@Cacheable(value = RedisConstants.OAUTH2_CLIENT_INFO, key = "#clientId", unless = "#result == null")
	public ClientDetails loadClientByClientId(String clientId) {
		OauthClientInfoVo clientDetails = oauthClientInfoService.selectClientByClientId(clientId,true);
		if (clientDetails == null) {
			throw new NoSuchClientException("No client with requested id: " + clientId);
		}
		// 适配成oauth2内置类型
		return clientDetailsWrapper(clientDetails);
	}

	/**
	 * 客户端类型转化 参考
	 * {@link org.springframework.security.oauth2.provider.client.JdbcClientDetailsService}
	 * @param clientDetails 数据库保存参数
	 * @return target oauth 类型客户端参数
	 */
	private ClientDetails clientDetailsWrapper(OauthClientInfoVo clientDetails) {
		BaseClientDetails target = new BaseClientDetails();
		// 必选项
		target.setClientId(clientDetails.getClientId());
		target.setClientSecret(String.format("{noop}%s", clientDetails.getClientSecret()));
		if (ArrayUtil.isNotEmpty(clientDetails.getAuthorizedGrantTypes())) {
			target.setAuthorizedGrantTypes(CollUtil.newArrayList(clientDetails.getAuthorizedGrantTypes().split(",")));
		}
		if (StrUtil.isNotBlank(clientDetails.getAuthorities())) {
			target.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(clientDetails.getAuthorities()));
		}
		if (StrUtil.isNotBlank(clientDetails.getResourceIds())) {
			target.setResourceIds(StringUtils.commaDelimitedListToSet(clientDetails.getResourceIds()));
		}
		if (StrUtil.isNotBlank(clientDetails.getWebServerRedirectUri())) {
			target.setRegisteredRedirectUri(StringUtils.commaDelimitedListToSet(clientDetails.getWebServerRedirectUri()));
		}
		if (StrUtil.isNotBlank(clientDetails.getScope())) {
			target.setScope(StringUtils.commaDelimitedListToSet(clientDetails.getScope()));
		}
		if (StrUtil.isNotBlank(clientDetails.getAutoapprove())) {
			target.setAutoApproveScopes(StringUtils.commaDelimitedListToSet(clientDetails.getAutoapprove()));
		}
		if (clientDetails.getAccessTokenValidity() != null) {
			target.setAccessTokenValiditySeconds(clientDetails.getAccessTokenValidity());
		}
		if (clientDetails.getRefreshTokenValidity() != null) {
			target.setRefreshTokenValiditySeconds(clientDetails.getRefreshTokenValidity());
		}
		String json = clientDetails.getAdditionalInformation();
		if (StrUtil.isNotBlank(json)) {
			try {
				@SuppressWarnings("unchecked")
				Map<String, Object> additionalInformation = JSONUtil.toBean(json, Map.class);
				target.setAdditionalInformation(additionalInformation);
			}
			catch (Exception e) {
				log.warn("Could not decode JSON for additional information: " + json, e);
			}
		}
		return target;
	}

}
